#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <boost/filesystem.hpp>
#include "util.hpp"
#include "Log.hpp"


// 原始数据存放的文件的路径
const std::string src_data_path = "./data/input";

// 处理后的数据放到的文件的路径
const std::string output_path = "./data/raw_html/raw.txt";

typedef struct Docinfo
{
    std::string title;   // 一个网页的标题
    std::string content; // 一个网页的内容
    std::string url;     // 一个网页的url官网网址

} Docinfo_t;

bool Enumfilesname(const std::string &src_path,std::vector<std::string> *filesname_list);
bool ParserHtml(const std::vector<std::string> &filesname_list, std::vector<Docinfo_t> *results);
bool SaveHtml(const std::vector<Docinfo_t> &results, const std::string &output_path);

// 去标签，将input中放的原始的文档去掉标签后的html数据放到当前路径下的raw_html中
int main()
{
    // 第一步:将原始文件中的文件名保存在vector中
    std::vector<std::string> filesname_list;
    if (!Enumfilesname(src_data_path,&filesname_list))
    {
        LOG(FATAL,"enum filesname error");
        return 1;
    }

    //for debug
    // for(auto& filename:filesname_list)
    // {
    //     std::cout << filename << std::endl;
    // }

    // 第二步:通过filesname_list中的文件名，将每一个文件中的html标签去掉，将每一个文件关键信息放到Docinfo中，然后插入vector中
    std::vector<Docinfo_t> results;
    if (!ParserHtml(filesname_list, &results))
    {
        LOG(FATAL,"parser files error");
        return 2;
    }
    //for dubug
    // for(auto& doc:results)
    // {
    //     std::cout << doc.title << "*****" << doc.content << "*****" << doc.url << std::endl;
    // }

    // 第三步：将每一个文件的Docinfo_t信息格式化写入到output_path中
    // 格式::    title\3content\3url\n    title\3content\3url\n
    if (!SaveHtml(results, output_path))
    {
        LOG(FATAL,"save files error");
        return 3;
    }

    return 0;
}

//使用的boost开发库中 filesystem模块的接口函数的方法
bool Enumfilesname(const std::string &src_path, std::vector<std::string> *filesname_list){
    namespace fs = boost::filesystem;   
    
    //利用src_path构建一个filesystem中的path对象
    fs::path root_path(src_path);    
    
    //利用boost::filesystem里面的exists结构判断文件名是否存在
    if (!fs::exists(root_path)){    
        LOG(FATAL,"%s not exists",src_path.c_str());
        return false;
    }
    
    // 定义一个空的迭代器，用于判断是否结束。
    fs::recursive_directory_iterator end;
    for (fs::recursive_directory_iterator iter(root_path); iter != end; iter++){
        // 利用boost::filesystem里面的is_regular_file接口判断是否是一个普通文件
        if (!fs::is_regular_file(*iter))   continue;
        
        // 利用path类中extension()成员函数的 判断文件后缀是否是.html
        if (iter->path().extension() != ".html")	continue;

        // 文件符合要求，将文件名插入到vector中,方便后续进行Parser
        filesname_list->push_back(iter->path().string());
    }

    return true;
}

//提取标题
static bool ParserTitle(const std::string &fileresult, std::string *title){
    int begin = fileresult.find("<title>");
    if (begin == std::string::npos){
        return false;
    }
    int end = fileresult.find("</title>");
    if (end == std::string::npos){
        return false;
    }
    begin += std::string("<title>").size();
    if (begin > end){
        return false;
    }
    *title = fileresult.substr(begin, end - begin);
    return true;
}

//提取内容
static bool ParserConent(const std::string &fileresult, std::string *content){
    
    // 编写一个简易状态机来实现
    enum Status{
        LABEL,    //标签
        CONTENT   //内容
    };

    enum Status s = LABEL;        //html最开都是以标签开始的
    for (char c : fileresult){
        switch (s){
            case LABEL:
                if (c == '>') s = CONTENT;
                break;
            case CONTENT:
                if (c == '<') s = LABEL;
                else{
                    if (c == '\n') c = ' ';
                    content->push_back(c);
                }
                break;
            default:
                break;
        }
    }

    return true;
}


//构建URL boost库的官⽅⽂档，和我们下载下来的⽂档，是有路径的对应关系的
//URL 网址示例：https://www.boost.org/doc/libs/1_78_0/doc/html/accumulators.html
//我们下载下来的url样例：boost_1_78_0/doc/html/accumulators.html
//我们拷⻉到我们项⽬中的样例：data/input/accumulators.html

static bool ParserUrl(const std::string &file_path, std::string *url){
    std::string url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html";
    //file_path是该文件在服务器上的路径 例如：./data/input/####.html  
    //只需要保留####.html   然后与官网上的拼接即可
    std::string url_tail = file_path.substr(src_data_path.size());    

    *url = url_head + url_tail;
    return true;
}



bool ParserHtml(const std::vector<std::string> &filesname_list, std::vector<Docinfo_t> *results)
{
    for (const std::string& file : filesname_list){
        // 第一步:读取文件，放到一个string中
        std::string fileResult;
        if (!ns_util::FileUtil::ReadFile(file, &fileResult)){
            LOG(FATAL,"Read file error");
            continue;
        }

        Docinfo_t Doc;
        // 第二步：提取文件的标题
        if (!ParserTitle(fileResult, &Doc.title)){
            continue;
        }

        // 第三步：提取文件内容
        if (!ParserConent(fileResult, &Doc.content)){
            continue;
        }

        // 第四步：提取文件url 
        if (!ParserUrl(file, &Doc.url)){
            continue;
        }

        // 第五步：将Doc放入results中，方便后面SaveHtml
        results->push_back(std::move(Doc)); // move进行优化
    }
    return true;
}



//讲处理好放到vector<Docinfo_t>的数据，格式化放入文件中
bool SaveHtml(const std::vector<Docinfo_t> &results, const std::string &output_path)
{
#define SEP "\3"

    std::ofstream out(output_path, std::ios::out | std::ios::binary);
    if (!out.is_open()){
        LOG(FATAL,"open file failed");
        return false;
    }
    for (auto &doc : results){
        std::string line;
        line = doc.title;
        line += SEP;
        line += doc.content;
        line += SEP;
        line += doc.url;
        line += '\n';

        out.write(line.c_str(),line.size());

    }
    
    out.close();

    return true;
}



